package com.titanrise.crm.service.impl;

import cn.hutool.system.UserInfo;
import com.titanrise.crm.common.enums.LogEffect;
import com.titanrise.crm.common.enums.RoleType;
import com.titanrise.crm.common.utils.*;
import com.titanrise.crm.dao.IndexDao;
import com.titanrise.crm.domain.dto.RequestInfo;
import com.titanrise.crm.domain.model.Role;
import com.titanrise.crm.domain.model.SysLoginLog;
import com.titanrise.crm.domain.model.User;
import com.titanrise.crm.domain.model.enums.BelongStatus;
import com.titanrise.crm.domain.model.enums.LoginStatus;
import com.titanrise.crm.environment.annotation.Autowired;
import com.titanrise.crm.environment.annotation.Service;
import com.titanrise.crm.environment.menu.MenuContext;
import com.titanrise.crm.environment.menu.action.Action;
import com.titanrise.crm.exception.RetryFunctionException;
import com.titanrise.crm.service.IndexService;
import lombok.SneakyThrows;

import java.time.LocalDateTime;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 用于登录、注册的服务层
 */
@Service("indexService")
public class IndexServiceImpl implements IndexService {
    @Autowired
    private IndexDao indexDao;

    private String createLogInfo(Role currentRole) {
        SysLoginLog log = SysLoginLog.builder()
                .userId(currentRole.getUid())
                .name(currentRole.getName())
                .loginTime(LocalDateTime.now())
                .loginStatus(LoginStatus.SUCCESS)
                .build();
        int i = indexDao.insertLoginLog(log);
        if (i == 0)
            throw new RuntimeException("Insert LoginLog ERROR!");
        return log.getLogId();
    }

    private void createLogInfo(String reason, String uid) {
        SysLoginLog log = SysLoginLog.builder()
                .userId(uid)
                .loginTime(LocalDateTime.now())
                .loginStatus(LoginStatus.FAILURE)
                .failureReason(reason)
                .build();
        int i = indexDao.insertLoginLog(log);
        if (i == 0)
            throw new RuntimeException("Insert LoginLog ERROR!");
    }

    private Either<String, Role> registerForCustomer(String username, String password, String name) {
        if (indexDao.findRoleByUsername(username).isJust())
            return Either.left("用户名已存在！请重试！");

        LocalDateTime now = LocalDateTime.now();
        User user = User.builder().createTime(now).build();

        int i = indexDao.insertUser(user);
        if (i == 0) {
            throw new RuntimeException("Insert User ERROR!");
        }

        Role role = Role.builder()
                .role(RoleType.Customer.intValue())
                .name(name).username(username)
                .password(password)
                .createTime(now)
                .uid(user.getId())
                .lockState(0)
                .isBelong(BelongStatus.FALSE)
                .build();

        i = indexDao.insertRole(role);
        if (i == 0)
            throw new RuntimeException("Insert Role ERROR!");

        return Either.right(role);
    }

    @Override
    public Action login(MenuContext context) {
        Function<String, Either<String, String>> checker = Functions.flow(ValidationUtil::trimThenRight, ValidationUtil.nonNull("输入不可为空！请重试！"));

        String username = ProjectUtil.getLine("请输入用户名：", checker);
        String password = ProjectUtil.getLine("请输入密码：", checker);

        return Functions.pipe(
                IOUtil.<Maybe<Role>>loading(
                        Pair.of(
                                (ignored) -> "登录中，请等待",
                                new LogEffect[]{LogEffect.CYAN, LogEffect.ITALIC}
                        ),
                        Pair.of("", new LogEffect[]{}),
                        () -> indexDao.findRoleByUsername(username)
                ),
                Either.<Supplier<Action>, Role>fromMaybe(() -> {
                    Console.println("用户名不存在！请重试！", LogEffect.RED);
                    this.createLogInfo("用户名不存在！", null);
                    return Action.JUMP_TO_FUNCTION("/", 1);
                }),
                EitherPipeUtil.<Supplier<Action>, Role, Role>flatMap(role -> !role.getPassword().equals(password) ?
                        Either.left(() -> {
                            Console.println("密码错误！请重试！", LogEffect.RED);
                            this.createLogInfo("密码错误！", role.getUid());
                            return Action.JUMP_TO_FUNCTION("/", 1);
                        }) : Either.right(role)
                ),
                EitherPipeUtil.<Supplier<Action>, Role, Supplier<Either<Supplier<Action>, Integer>>>map(role -> () ->
                        Functions.pipe(
                                indexDao.findUserById(role.getUid()),
                                Either.fromMaybe(() -> {
                                    Console.println("数据异常！请重试！", LogEffect.RED);
                                    this.createLogInfo("对应的用户不存在！", role.getUid());
                                    return Action.JUMP_TO_FUNCTION("/", 1);
                                }),
                                EitherPipeUtil.map(user -> {
                                    String logId = this.createLogInfo(role);

                                    context.getState().put("CurrentRole", role);
                                    context.getState().put("CurrentUser", user);
                                    context.getState().put("CurrentLoginLogID", logId);

                                    return role.getRole();
                                })
                        )),
                EitherPipeUtil.map(SupplierPipeUtil.map(EitherPipeUtil.map(roleType -> () -> {
                    switch (roleType) {
                        case 0:
                            Console.println("登录成功！作为客户登录！", LogEffect.ITALIC, LogEffect.CYAN);
                            return Action.JUMP_TO_MENU("/customer");
                        case 1:
                            Console.println("登录成功！作为销售登录！", LogEffect.ITALIC, LogEffect.CYAN);
                            return Action.JUMP_TO_MENU("/salesperson");
                        case 2:
                            Console.println("登录成功！作为管理员登录！", LogEffect.ITALIC, LogEffect.CYAN);
                            return Action.JUMP_TO_MENU("/root");
                        default:
                            throw new IllegalStateException("RoleType: " + roleType + " doesn't exist!");
                    }
                }))),
                EitherPipeUtil.map(SupplierPipeUtil.flatMap(EitherPipeUtil.getOrElse(Functions::identity))),
                EitherPipeUtil.getOrElse(Functions::identity),
                Supplier::get
        );
    }

    @Override
    public Action register(MenuContext context) {
        String username = ProjectUtil.getLine("请输入用户名：", Functions.flow(ValidationUtil::trimThenRight, ValidationUtil.nonEmpty("用户名不可为空！请重试！"), ValidationUtil.lenLowerLimit(2, "用户名长度不可小于2！请重试！"), ValidationUtil.lenUpperLimit(12, "用户名长度不可大于12！请重试！"), ValidationUtil.matchRegex("^[a-zA-Z0-9_\\u4E00-\\u9FFF]+$", "用户名仅能包含字母、数字、汉字以及下划线！请重试！")));

        if (indexDao.findRoleByUsername(username).isJust()) {
            Console.println("用户名已存在！请重试！", LogEffect.RED);
            return Action.JUMP_TO_FUNCTION("/", 2);
        }

        String password = ProjectUtil.getLine("请输入密码：", Functions.flow(ValidationUtil::trimThenRight, ValidationUtil.nonEmpty("密码不可为空！请重试！"), ValidationUtil.lenLowerLimit(6, "密码长度不可小于6！请重试！"), ValidationUtil.lenUpperLimit(12, "密码长度不可大于12！请重试！"), ValidationUtil.matchRegex("^[a-zA-Z0-9@#\\$%&!\\*\\?]+$", "密码仅能包含字母、数字以及特殊字符（@, #, $, %, &, !, *, ?）！请重试！")));
        String name = ProjectUtil.getLine("请输入姓名：", Functions.flow(ValidationUtil::trimThenRight, ValidationUtil.nonEmpty("姓名不可为空！请重试！"), ValidationUtil.lenLowerLimit(2, "姓名长度不可小于2！请重试！"), ValidationUtil.lenUpperLimit(5, "姓名长度不可大于5！请重试！"), ValidationUtil.matchRegex("^[\\u4E00-\\u9FFF]+$", "姓名只能输入中文！请重试！")));

        return Functions.pipe(
                this.registerForCustomer(username, password, name),
                EitherPipeUtil.flatMap(role ->
                        Functions.pipe(
                                indexDao.findUserById(role.getUid()),
                                MaybePipeUtil.<User, Supplier<Action>>map(user -> () -> {
                                    String logId = this.createLogInfo(role);

                                    context.getState().put("CurrentRole", role);
                                    context.getState().put("CurrentUser", user);
                                    context.getState().put("CurrentLoginLogID", logId);

                                    return (Action) Action.JUMP_TO_MENU("/customer");
                                }),
                                Either.fromMaybe("数据异常！请重试！")
                        )),
                EitherPipeUtil.getOrElse(reason -> () -> {
                    Console.println(reason, LogEffect.RED);
                    return Action.JUMP_TO_FUNCTION("/", 2);
                }),
                Supplier::get
        );
    }

}
